package service

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/bgmp/config"
	jwt "github.com/dgrijalva/jwt-go"
	log "github.com/sirupsen/logrus"
	"io"
	"net/http"
)

var (
	client = &http.Client{}
)

// Sign signs the identity with the specified secret.
func Sign(id uint) (tokenString string, err error) {
	// The usertoken content.
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
		"id": id,
	})
	// Sign the usertoken with the specified secret.
	tokenString, err = token.SignedString([]byte(config.GetSecret()))
	return
}

// secretFunc validates the secret format.
func secretFunc(secret string) jwt.Keyfunc {
	return func(token *jwt.Token) (interface{}, error) {
		// Make sure the `alg` is what we except.
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, jwt.ErrSignatureInvalid
		}

		return []byte(secret), nil
	}
}

// Parse validates the usertoken with the specified secret,
// and returns the context if the usertoken was valid.
func Parse(tokenString string) (id uint, err error) {
	// Parse the usertoken.
	token, err := jwt.Parse(tokenString, secretFunc(config.GetSecret()))

	// Parse error.
	if err != nil {
		err = errors.New("token is invalid")
		return

		// Read the usertoken if it's valid.
	} else {
		if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
			id = uint(claims["id"].(float64))
			// Other errors.
		} else {
			err = errors.New("token is invalid")
		}
	}
	return
}

func Code2Session(code string) (openid, sessionKey string, err error) {
	if config.GetSingle() {
		openid = "okcWi5Nt0VLh2dngjqy5f3g37bck"
		sessionKey = "meaningless"
		return
	}

	url := fmt.Sprintf(`https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code`, config.GetAppId(), config.GetAppSecret(), code)
	log.Info("Code2Session url: ", url)

	req, err := http.NewRequest("GET", url, nil)
	req.Header.Add("Accept", "application/json")
	if err != nil {
		return
	}
	resp, err := client.Do(req)
	if err != nil {
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		err = errors.New("无法获得用户openid")
		return
	}

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return
	}
	rsp := Code2SessionRsp{}
	err = json.Unmarshal(body, &rsp)
	if err != nil {
		return
	}
	log.Info("Code2Session rsp: ", rsp)
	if rsp.ErrCode != 0 {
		err = errors.New("无法获得用户openid")
		return
	}
	openid = rsp.Openid
	sessionKey = rsp.SessionKey

	return
}

type Code2SessionRsp struct {
	Openid     string `json:"openid"`
	SessionKey string `json:"session_key"`
	UnionId    string `json:"unionid"`
	ErrCode    int    `json:"errcode"`
	ErrMsg     string `json:"errmsg"`
}
